﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Json;
using System.Security.Policy;
using System.Text.Json;
using System.Threading.Tasks;
using Certify.Client;
using Certify.Locales;
using Certify.Models;
using Certify.Models.Config;
using Certify.Models.Config.Migration;
using Certify.Models.Hub;
using Certify.Models.Reporting;
using Certify.Models.Shared;
using Certify.Shared;
using Certify.Shared.Core.Utils;
using Microsoft.IdentityModel.JsonWebTokens;
using Registration.Core.Models.Shared;

namespace Certify.Management
{
    public partial class CertifyManager
    {
        private IManagementServerClient _managementServerClient;
        private bool _isDirectMgmtHubBackend = false;
        private bool _isMgtmHubBackend = false;

        private ClientSecret _mgmtHubJoiningSecret;
        public const string MgmtHubJoiningCredId = "_ManagementHubJoiningKey";

        public async Task<ActionResult> CheckManagementHubConnectionStatus()
        {
            if (_managementServerClient?.IsConnected() == true)
            {
                return new ActionResult("Connected to Management Hub.", isSuccess: true);
            }
            else
            {
                return new ActionResult("Not connected to Management Hub.", isSuccess: false);
            }
        }

        private void SetHubAssignedInstanceId(string? val)
        {
            _serverConfig.HubAssignedInstanceId = val;
            SharedUtils.ServiceConfigManager.StoreUpdatedAppServiceConfig(_serverConfig);
        }
        public async Task<ActionResult> JoinManagementHub(string url, ClientSecret clientSecret)
        {
            var hubConnectionAuthToken = string.Empty;

            _serverConfig = SharedUtils.ServiceConfigManager.GetAppServiceConfig();

            ActionResult<HubJoiningInfo> joiningCredentialsCheck = null;

            if (!string.IsNullOrWhiteSpace(_serverConfig.HubAssignedInstanceId))
            {
                _serviceLog.Information("Hub already joined, will reconnect.");
                // when have already joined a hub, first check if we are rejoining the same hub by just verifying the credentials
                joiningCredentialsCheck = await CheckManagementHubCredentials(url, clientSecret, registerInstance: false);

                if (!joiningCredentialsCheck.IsSuccess && joiningCredentialsCheck.Result?.RejoinRequired == true)
                {
                    _serviceLog.Information("Hub rejoin required, will attempt to re-register instance.");
                    // need to re-register
                    SetHubAssignedInstanceId(val: null);

                    joiningCredentialsCheck = await CheckManagementHubCredentials(url, clientSecret, registerInstance: true);
                }
            }
            else
            {
                // if we are not rejoining the same hub, we need to register a new instance
                _serviceLog.Information("Hub not yet joined, will attempt to join.");
                joiningCredentialsCheck = await CheckManagementHubCredentials(url, clientSecret, registerInstance: true);
            }

            if (joiningCredentialsCheck.IsSuccess)
            {
                hubConnectionAuthToken = joiningCredentialsCheck.Result.JoiningToken;

                var hubEndpoint = joiningCredentialsCheck.Result.HubEndpoint;

                // store hub api endpoint and assigned id

                _serverConfig.ManagementServerHubAPI = url;
                _serverConfig.ManagementServerHubEndpoint = hubEndpoint;

                // store our hub managed instance id if it has changed/been created
                if (_serverConfig.HubAssignedInstanceId != joiningCredentialsCheck.Result.HubAssignedInstanceId)
                {
                    if (!string.IsNullOrWhiteSpace(joiningCredentialsCheck.Result.HubAssignedInstanceId))
                    {
                        _serverConfig.HubAssignedInstanceId = joiningCredentialsCheck.Result.HubAssignedInstanceId;
                    }
                    else
                    {
                        _serviceLog.Warning("Hub joined ok but hub assigned instance id was empty.");
                    }
                }

                SharedUtils.ServiceConfigManager.StoreUpdatedAppServiceConfig(_serverConfig);

                // store/update clientId and secret
                _mgmtHubJoiningSecret = clientSecret;

                await _credentialsManager.Update(new StoredCredential
                {
                    StorageKey = MgmtHubJoiningCredId,
                    ProviderType = StandardAuthTypes.STANDARD_AUTH_MGMTHUB,
                    Title = "Management Hub Joining Key",
                    Secret = JsonSerializer.Serialize(clientSecret)
                });

                try
                {
                    await EnsureMgmtHubConnection(hubConnectionAuthToken);
                }
                catch
                {
                    return new ActionResult("A problem occurred when connecting to the management hub. Check URL and credentials.", isSuccess: false);
                }

                return new ActionResult("Connected to Management Hub.", isSuccess: true);
            }
            else
            {
                _serviceLog.Information("Hub credentials check failed.");
                return joiningCredentialsCheck;
            }
        }

        /// <summary>
        /// Checks the credentials for connecting to a Management Hub and returns the status along with hub information.
        /// information.
        /// </summary>
        /// <param name="url">Specifies the endpoint for the Management Hub to verify the connection.</param>
        /// <param name="clientSecret">Contains the credentials required for authenticating the connection to the Management Hub.</param>
        /// <param name="registerInstance">Indicates whether to register the current instance with the Management Hub during the check.</param>
        /// <returns>Returns an action result indicating the success of the connection attempt and any relevant hub information.</returns>
        public async Task<ActionResult<HubJoiningInfo>> CheckManagementHubCredentials(string url, ClientSecret clientSecret, bool registerInstance = false)
        {
            var handler = new HttpClientHandler()
            {
                ServerCertificateCustomValidationCallback = (message, cert, chain, sslPolicyErrors) =>
                {
                    // Allow all certificates (including untrusted ones)
                    return true;
                }
            };

            if (Environment.GetEnvironmentVariable("CERTIFY_MANAGEMENT_HUB_ALLOW_UNTRUSTED") == "true")
            {
                handler.ServerCertificateCustomValidationCallback = null;
            }

            if (string.IsNullOrWhiteSpace(_serverConfig.HubAssignedInstanceId) && registerInstance == false)
            {
                _serviceLog.Warning("Attempting to rejoin hub but hub assigned instance ID is empty, need to re-register with hub");
                registerInstance = true;
            }

            var endpoint = $"{url.TrimEnd('/')}/api/v1/hub/{(registerInstance ? "register" : "joincheck")}";

            _serviceLog.Debug("Checking credentials via Management Hub {url}", endpoint);

            using var httpClient = new System.Net.Http.HttpClient(handler);

            var request = new System.Net.Http.HttpRequestMessage(System.Net.Http.HttpMethod.Get, endpoint);
            request.Headers.Add("X-Client-ID", clientSecret.ClientId);
            request.Headers.Add("X-Client-Secret", clientSecret.Secret);

            request.Headers.Add("X-Certify-Trace-InstanceName", GetManagedInstanceInfo().Title);

            if (!registerInstance)
            {
                // already joined , include assigned id to verify
                if (!string.IsNullOrWhiteSpace(_serverConfig.HubAssignedInstanceId))
                {
                    request.Headers.Add("X-Certify-HubAssignedId", _serverConfig.HubAssignedInstanceId);
                }
                else
                {
                    // if we are not registering, we should have an assigned id to check against
                    return new ActionResult<HubJoiningInfo>("Hub Assigned Instance ID is required when rejoining the hub.", isSuccess: false);
                }
            }

            try
            {
                var response = await httpClient.SendAsync(request);

                if (response.IsSuccessStatusCode)
                {
                    var json = await response.Content.ReadAsStringAsync();
                    var hubInfo = JsonSerializer.Deserialize<HubJoiningInfo>(json, JsonOptions.DefaultJsonSerializerOptions);
                    return new ActionResult<HubJoiningInfo>("Connected to Management Hub.", isSuccess: true, hubInfo);
                }
                else
                {
                    var content = await response.Content.ReadAsStringAsync();

                    if (response.Content.Headers.ContentType?.MediaType == "application/problem+json")
                    {
                        try
                        {
                            var problemDetails = await response.Content.ReadFromJsonAsync<Microsoft.AspNetCore.Mvc.ProblemDetails>();

                            var result = new ActionResult<HubJoiningInfo>($"Could not connect to Management Hub. {problemDetails.Title} - {problemDetails.Detail}", isSuccess: false);
                            if (problemDetails.Type.EndsWith("/hub-unknown-instance-id"))
                            {
                                result.Result = new HubJoiningInfo
                                {
                                    Message = "The provided Hub Assigned Instance ID is not recognized by the Management Hub. It may be incorrect or associated with a different hub.",
                                    RejoinRequired = true
                                };
                            }

                            return result;
                        }
                        catch (System.Text.Json.JsonException)
                        {
                            _serviceLog.Debug("Failed to parse problem+json response from hub.");
                        }
                    }

                    if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
                    {
                        return new ActionResult<HubJoiningInfo>($"Could not connect to Management Hub (Unauthorized). {content} - Check credentials {endpoint} {clientSecret.ClientId} {clientSecret.Secret} {_serverConfig.HubAssignedInstanceId}. {response}", isSuccess: false);
                    }
                    else
                    {
                        return new ActionResult<HubJoiningInfo>("Could not connect to Management Hub. Check URL.", isSuccess: false);
                    }
                }
            }
            catch (HttpRequestException httpEx) when (httpEx.InnerException is System.Net.Sockets.SocketException socketEx && socketEx.ErrorCode == 111)
            {
                return new ActionResult<HubJoiningInfo>($"Could not connect to Management Hub. Connection refused (port may not be open or service not running). {endpoint}", isSuccess: false);
            }
            catch (HttpRequestException httpEx)
            {
                return new ActionResult<HubJoiningInfo>($"Could not connect to Management Hub. Network error: {httpEx.Message}", isSuccess: false);
            }
            catch (Exception exp)
            {
                return new ActionResult<HubJoiningInfo>($"Could not connect to Management Hub. {exp}", isSuccess: false);
            }
        }

        public void EnableManagementHubBackend(bool isDirectHubBackend)
        {
            _isDirectMgmtHubBackend = isDirectHubBackend;
        }

        public void SetDirectManagementClient(IManagementServerClient client)
        {
            client.UpdateCachedInstanceInfo(GetManagedInstanceInfo());
            _managementServerClient = client;
        }

        public async Task<HubInfo> GetHubInfo()
        {
            if (_isMgtmHubBackend)
            {
                var hubInfo = new HubInfo();

                hubInfo.InstanceId = _serverConfig.HubAssignedInstanceId;

                var instanceInfo = GetManagedInstanceInfo();
                hubInfo.IsLicensed = instanceInfo.License?.StatusCode == LicenseCheckStatusCode.Licensed;

                var versionInfo = Util.GetAppVersion().ToString();

                hubInfo.Version = new Models.Hub.VersionInfo
                {
                    Version = versionInfo,
                    Product = "Certify Management Hub"
                };

                return hubInfo;
            }
            else
            {
                return null;
            }
        }

        private JsonWebTokenHandler _joiningTokenHandler = new JsonWebTokenHandler();

        /// <summary>
        /// Ensures there is a current connection to the management hub, reconnecting if necessary. Sends a heartbeat message to the hub if already connected.
        /// </summary>
        /// <returns></returns>
        private async Task EnsureMgmtHubConnection(string? hubConnectionAuthToken = null)
        {
            // connect/reconnect to management hub if enabled (either connection not established or our joining token is null/expired)
            if (_managementServerClient?.IsConnected() != true)
            {
                var mgmtHubUri = string.Empty;
                var api = string.Empty;
                var endpoint = string.Empty;
                var defaultEnpoint = "api/internal/managementhub";

                if (!_isDirectMgmtHubBackend)
                {
                    // construct hub api url and status hub api endpoint
                    if (Environment.GetEnvironmentVariable("CERTIFY_MANAGEMENT_HUB") != null)
                    {
                        api = Environment.GetEnvironmentVariable("CERTIFY_MANAGEMENT_HUB");

                        if (api.EndsWith(defaultEnpoint))
                        {
                            mgmtHubUri = api;

                            endpoint = defaultEnpoint;
                            api = api.Replace(defaultEnpoint, "");
                        }
                        else
                        {
                            endpoint = Environment.GetEnvironmentVariable("CERTIFY_MANAGEMENT_HUB_ENDPOINT") ?? defaultEnpoint;
                            mgmtHubUri = $"{api.Trim('/')}/{endpoint.Trim('/')}";
                        }
                    }
                    else
                    {
                        api = _serverConfig.ManagementServerHubAPI.Trim('/');
                        endpoint = _serverConfig.ManagementServerHubEndpoint.Trim('/');
                        mgmtHubUri = $"{api}/{endpoint}";
                    }

                    // if hub url has resolved to "/", remove trailing slash and continue with empty string
                    mgmtHubUri = mgmtHubUri?.TrimEnd('/');

                    if (!string.IsNullOrWhiteSpace(mgmtHubUri))
                    {

                        if (_mgmtHubJoiningSecret == null)
                        {
                            // check if we have an environment variable for client id and client secret
                            var clientId = Environment.GetEnvironmentVariable("CERTIFY_MANAGEMENT_HUB_CLIENT_ID");
                            var clientSecret = Environment.GetEnvironmentVariable("CERTIFY_MANAGEMENT_HUB_CLIENT_SECRET");
                            if (!string.IsNullOrWhiteSpace(clientId) && !string.IsNullOrWhiteSpace(clientSecret))
                            {
                                _mgmtHubJoiningSecret = new ClientSecret
                                {
                                    ClientId = clientId,
                                    Secret = clientSecret
                                };

                                AddSystemStatusItem(
                                    SystemStatusCategories.SERVICE_CORE,
                                    SystemStatusKeys.SERVICE_CORE_HUB_JOINING_KEY,
                                    "Management Hub Joining Key",
                                    "Using management hub joining key from environment variables"
                                    );
                            }

                            // if not set by env, check if we already have a management hub joining key as a stored credential
                            if (_mgmtHubJoiningSecret == null)
                            {
                                try
                                {
                                    var secret = await _credentialsManager.GetUnlockedCredential(CertifyManager.MgmtHubJoiningCredId);

                                    if (secret != null)
                                    {
                                        _mgmtHubJoiningSecret = JsonSerializer.Deserialize<ClientSecret>(secret, JsonOptions.DefaultJsonSerializerOptions);
                                    }
                                }
                                catch (Exception ex)
                                {
                                    _serviceLog.Error(ex, "Error retrieving management hub joining key from credentials store.");
                                }
                            }

                            if (_mgmtHubJoiningSecret == null)
                            {

                                AddSystemStatusItem(
                                    SystemStatusCategories.SERVICE_CORE,
                                    SystemStatusKeys.SERVICE_CORE_HUB_JOINING_KEY,
                                    "Management Hub Joining Key",
                                    "Management hub joining key not set, instance cannot join hub.",
                                    hasWarning: true
                                    );

                                _serviceLog.Error($"Hub joining secret invalid or not found while attempting to join {mgmtHubUri}");
                                return;
                            }
                        }

                        // acquire new token
                        var check = await CheckManagementHubCredentials(api, _mgmtHubJoiningSecret);

                        if (check.IsSuccess)
                        {
                            hubConnectionAuthToken = check.Result.JoiningToken;

                            if (string.IsNullOrWhiteSpace(_serverConfig.HubAssignedInstanceId) && !string.IsNullOrWhiteSpace(check.Result.HubAssignedInstanceId))
                            {
                                // first time join, store assigned id
                                SetHubAssignedInstanceId(check.Result.HubAssignedInstanceId);

                                _serviceLog.Warning($"EnsureMgmtHubConnection: hub assigned instance ID was previously empty, updated to new assigned ID.");
                            }
                            else if (_serverConfig.HubAssignedInstanceId != check.Result.HubAssignedInstanceId)
                            {
                                AddSystemStatusItem(
                                    SystemStatusCategories.SERVICE_CORE,
                                    SystemStatusKeys.SERVICE_CORE_HUB_JOINING_AUTH,
                                    "Management Hub Joining Auth",
                                    "Management hub joining auth successful but hub assigned instance ID did not match. Current settings may be for a different hub. Hub assigned instance id updated.",
                                    hasError: true
                                );

                                SetHubAssignedInstanceId(check.Result.HubAssignedInstanceId);
                            }
                            else
                            {
                                AddSystemStatusItem(
                                    SystemStatusCategories.SERVICE_CORE,
                                    SystemStatusKeys.SERVICE_CORE_HUB_JOINING_AUTH,
                                    "Management Hub Joining Auth",
                                    "Management hub joining auth successful."
                                );
                            }
                        }
                        else
                        {

                            AddSystemStatusItem(
                                SystemStatusCategories.SERVICE_CORE,
                                SystemStatusKeys.SERVICE_CORE_HUB_JOINING_AUTH,
                                "Management Hub Joining Auth",
                                $"Management hub joining auth failed: {check.Message}",
                                hasError: true
                            );

                            if (check.Result?.RejoinRequired == true)
                            {
                                _serviceLog.Information("Hub rejoin required, will attempt to re-register instance.");

                                // need to re-register
                                SetHubAssignedInstanceId(val: null);
                            }

                            return;
                        }
                    }
                }

                if (!string.IsNullOrWhiteSpace(mgmtHubUri))
                {
                    await StartManagementHubConnection(mgmtHubUri, hubConnectionAuthToken);
                }
            }
            else
            {

                // send heartbeat message to management hub
                SendHeartbeatToManagementHub();
            }
        }

        private void SendHeartbeatToManagementHub()
        {
            try
            {
                if (_managementServerClient == null || !_managementServerClient.IsConnected())
                {
                    _serviceLog.Warning("Cannot send heartbeat - not connected to Management Hub");

                    // Trigger reconnection attempt
                    _ = Task.Run(async () => await EnsureMgmtHubConnection());

                    return;
                }

                _managementServerClient.UpdateCachedInstanceInfo(GetManagedInstanceInfo());
                _managementServerClient.SendInstanceInfo(Guid.NewGuid(), isCommandResponse: false);

                _serviceLog.Debug("Heartbeat sent to Management Hub");
            }
            catch (Exception ex)
            {
                _serviceLog.Error(ex, "Failed to send heartbeat to Management Hub");

                AddSystemStatusItem(
                    SystemStatusCategories.SERVICE_CORE,
                    SystemStatusKeys.SERVICE_CORE_HUB_CONNECTION,
                    "Management Hub Connection",
                    $"Heartbeat failed: {ex.Message}. Will attempt to reconnect shortly.",
                    hasWarning: true
                );
            }
        }

        public ManagedInstanceInfo GetManagedInstanceInfo()
        {
            return new ManagedInstanceInfo
            {
                Id = _serverConfig.HubAssignedInstanceId,
                InstanceId = _serverConfig.HubAssignedInstanceId,
                Title = $"{Environment.MachineName}",
                OS = EnvironmentUtil.GetFriendlyOSName(detailed: false),
                OSVersion = EnvironmentUtil.GetOSVersion(),
                ClientVersion = Util.GetAppVersion().ToString(),
                ClientName = _isMgtmHubBackend ? "Certify Management Hub" : ConfigResources.AppName,
                License = _cachedLicenseCheck
            };
        }

        private async Task StartManagementHubConnection(string hubUri, string hubConnectionAuthToken)
        {
            if (string.IsNullOrWhiteSpace(hubConnectionAuthToken))
            {
                _serviceLog.Error("No hub connection auth token available, cannot connect to management hub.");
                return;
            }

            var appVersion = Util.GetAppVersion().ToString();

            var instanceInfo = GetManagedInstanceInfo();

            if (_managementServerClient != null)
            {
                // if not currently connected, attempt connection
                if (!_managementServerClient.IsConnected())
                {
                    _serviceLog.Information("Hub not connected, attempting connection. {hubUri}", hubUri);
                    await _managementServerClient.ConnectAsync(hubConnectionAuthToken);
                }

                // if connected now, update status
                if (_managementServerClient.IsConnected())
                {
                    AddSystemStatusItem(
                            SystemStatusCategories.SERVICE_CORE,
                            SystemStatusKeys.SERVICE_CORE_HUB_CONNECTION,
                            "Management Hub Connection",
                            $"Successfully connected to Management Hub: {hubUri}"
                        );
                }
            }
            else
            {
                try
                {
                    _managementServerClient = new ManagementServerClient(hubUri, instanceInfo);

                    _managementServerClient.OnGetCommandResult += PerformHubCommandWithResult;
                    _managementServerClient.OnConnectionReconnecting += _managementServerClient_OnConnectionReconnecting;
                    _managementServerClient.OnConnectionReconnected += _managementServerClient_OnConnectionReconnected;
                    _managementServerClient.OnConnectionClosed += _managementServerClient_OnConnectionClosed;

                    await _managementServerClient.ConnectAsync(hubConnectionAuthToken);

                    if (_managementServerClient.IsConnected())
                    {
                        AddSystemStatusItem(
                            SystemStatusCategories.SERVICE_CORE,
                            SystemStatusKeys.SERVICE_CORE_HUB_CONNECTION,
                            "Management Hub Connection",
                            $"Successfully connected to Management Hub: {hubUri}"
                        );
                    }
                    else
                    {
                        AddSystemStatusItem(
                            SystemStatusCategories.SERVICE_CORE,
                            SystemStatusKeys.SERVICE_CORE_HUB_CONNECTION,
                            "Management Hub Connection",
                            $"Could not connect to Management Hub at {hubUri}",
                            hasError: true
                        );
                    }
                }
                catch (Exception ex)
                {
                    AddSystemStatusItem(
                        SystemStatusCategories.SERVICE_CORE,
                        SystemStatusKeys.SERVICE_CORE_HUB_CONNECTION,
                        "Management Hub Connection",
                        $"Failed to connect to Management Hub at {hubUri}: {ex.Message}",
                        hasError: true
                    );
                }
            }
        }

        public async Task<InstanceCommandResult> PerformHubCommandWithResult(InstanceCommandRequest arg)
        {
            object val = null;

            if (arg.CommandType == ManagementHubCommands.GetInstanceInfo)
            {
                var update = GetManagedInstanceInfo();
                _managementServerClient.UpdateCachedInstanceInfo(update);
                val = update;
            }
            else if (arg.CommandType == ManagementHubCommands.GetManagedItem)
            {
                // Get a single managed item by id
                var args = JsonSerializer.Deserialize<KeyValuePair<string, string>[]>(arg.Value, JsonOptions.DefaultJsonSerializerOptions);
                var managedCertIdArg = args.FirstOrDefault(a => a.Key == "managedCertId");
                val = await GetManagedCertificate(managedCertIdArg.Value);
            }
            else if (arg.CommandType == ManagementHubCommands.GetManagedItems)
            {
                // Get all managed items
                var items = await GetManagedCertificates(new ManagedCertificateFilter { IncludeExternal = CoreAppSettings.Current.EnableExternalCertManagers });
                val = new ManagedInstanceItems { InstanceId = _serverConfig.HubAssignedInstanceId, Items = items };
            }
            else if (arg.CommandType == ManagementHubCommands.GetStatusSummary)
            {
                var s = await GetManagedCertificateSummary(new ManagedCertificateFilter { });
                val = s;
            }
            else if (arg.CommandType == ManagementHubCommands.GetManagedItemLog)
            {
                var args = JsonSerializer.Deserialize<KeyValuePair<string, string>[]>(arg.Value, JsonOptions.DefaultJsonSerializerOptions);
                var managedCertIdArg = args.FirstOrDefault(a => a.Key == "managedCertId");
                var limit = args.FirstOrDefault(a => a.Key == "limit");

                val = await GetItemLog(managedCertIdArg.Value, int.Parse(limit.Value));
            }
            else if (arg.CommandType == ManagementHubCommands.GetManagedItemRenewalPreview)
            {
                var args = JsonSerializer.Deserialize<KeyValuePair<string, string>[]>(arg.Value, JsonOptions.DefaultJsonSerializerOptions);
                var managedCertArg = args.FirstOrDefault(a => a.Key == "managedCert");
                var managedCert = JsonSerializer.Deserialize<ManagedCertificate>(managedCertArg.Value, JsonOptions.DefaultJsonSerializerOptions);

                val = await GeneratePreview(managedCert);
            }
            else if (arg.CommandType == ManagementHubCommands.ExportCertificate)
            {
                var args = JsonSerializer.Deserialize<KeyValuePair<string, string>[]>(arg.Value, JsonOptions.DefaultJsonSerializerOptions);
                var managedCertIdArg = args.FirstOrDefault(a => a.Key == "managedCertId");
                var format = args.FirstOrDefault(a => a.Key == "format");
                val = await ExportCertificate(managedCertIdArg.Value, format.Value);
            }
            else if (arg.CommandType == ManagementHubCommands.UpdateManagedItem)
            {
                // update a single managed item 
                var args = JsonSerializer.Deserialize<KeyValuePair<string, string>[]>(arg.Value, JsonOptions.DefaultJsonSerializerOptions);
                var managedCertArg = args.FirstOrDefault(a => a.Key == "managedCert");
                var managedCert = JsonSerializer.Deserialize<ManagedCertificate>(managedCertArg.Value, JsonOptions.DefaultJsonSerializerOptions);

                val = await UpdateManagedCertificate(managedCert);
            }
            else if (arg.CommandType == ManagementHubCommands.RemoveManagedItem)
            {
                // delete a single managed item 
                var args = JsonSerializer.Deserialize<KeyValuePair<string, string>[]>(arg.Value, JsonOptions.DefaultJsonSerializerOptions);
                var managedCertIdArg = args.FirstOrDefault(a => a.Key == "managedCertId");

                val = await DeleteManagedCertificate(managedCertIdArg.Value);

                _ = PerformManagedChallengeCleanup(managedCertIdArg.Value);
            }
            else if (arg.CommandType == ManagementHubCommands.TestManagedItemConfiguration)
            {
                // test challenge response config for a single managed item 
                var args = JsonSerializer.Deserialize<KeyValuePair<string, string>[]>(arg.Value, JsonOptions.DefaultJsonSerializerOptions);
                var managedCertArg = args.FirstOrDefault(a => a.Key == "managedCert");
                var managedCert = JsonSerializer.Deserialize<ManagedCertificate>(managedCertArg.Value, JsonOptions.DefaultJsonSerializerOptions);

                var log = ManagedCertificateLog.GetLogger(managedCert.Id, _loggingLevelSwitch);

                val = await TestChallenge(log, managedCert, isPreviewMode: true);
            }
            else if (arg.CommandType == ManagementHubCommands.ResetManagedItemStatus)
            {
                // test challenge response config for a single managed item 
                var args = JsonSerializer.Deserialize<KeyValuePair<string, string>[]>(arg.Value, JsonOptions.DefaultJsonSerializerOptions);
                var managedCertIdArg = args.FirstOrDefault(a => a.Key == "managedCertId");

                val = await ResetManagedItemStatus(managedCertIdArg.Value);
            }
            else if (arg.CommandType == ManagementHubCommands.PerformManagedItemRequest)
            {
                // attempt certificate order
                var args = JsonSerializer.Deserialize<KeyValuePair<string, string>[]>(arg.Value, JsonOptions.DefaultJsonSerializerOptions);
                var managedCertIdArg = args.FirstOrDefault(a => a.Key == "managedCertId");
                var managedCert = await GetManagedCertificate(managedCertIdArg.Value);

                var progressState = new RequestProgressState(RequestState.Running, "Starting..", managedCert);
                var progressIndicator = new Progress<RequestProgressState>(progressState.ProgressReport);

                _ = await PerformCertificateRequest(
                                                        null,
                                                        managedCert,
                                                        progressIndicator,
                                                        resumePaused: true,
                                                        isInteractive: true
                                                        );

                val = true;
            }
            else if (arg.CommandType == ManagementHubCommands.GetCertificateAuthorities)
            {
                val = await GetCertificateAuthorities();
            }
            else if (arg.CommandType == ManagementHubCommands.UpdateCertificateAuthority)
            {
                var args = JsonSerializer.Deserialize<KeyValuePair<string, string>[]>(arg.Value, JsonOptions.DefaultJsonSerializerOptions);
                var itemArg = args.FirstOrDefault(a => a.Key == "certificateAuthority");
                var item = JsonSerializer.Deserialize<CertificateAuthority>(itemArg.Value, JsonOptions.DefaultJsonSerializerOptions);

                val = await UpdateCertificateAuthority(item);
            }
            else if (arg.CommandType == ManagementHubCommands.RemoveCertificateAuthority)
            {
                var args = JsonSerializer.Deserialize<KeyValuePair<string, string>[]>(arg.Value, JsonOptions.DefaultJsonSerializerOptions);
                var itemArg = args.FirstOrDefault(a => a.Key == "id");
                val = await RemoveCertificateAuthority(itemArg.Value);
            }
            else if (arg.CommandType == ManagementHubCommands.GetAcmeAccounts)
            {
                val = await GetAccountRegistrations();
            }
            else if (arg.CommandType == ManagementHubCommands.AddAcmeAccount)
            {
                var args = JsonSerializer.Deserialize<KeyValuePair<string, string>[]>(arg.Value, JsonOptions.DefaultJsonSerializerOptions);
                var registrationArg = args.FirstOrDefault(a => a.Key == "registration");
                var registration = JsonSerializer.Deserialize<ContactRegistration>(registrationArg.Value, JsonOptions.DefaultJsonSerializerOptions);

                val = await AddAccount(registration);
            }
            else if (arg.CommandType == ManagementHubCommands.RemoveAcmeAccount)
            {
                var args = JsonSerializer.Deserialize<KeyValuePair<string, string>[]>(arg.Value, JsonOptions.DefaultJsonSerializerOptions);
                var itemArg = args.FirstOrDefault(a => a.Key == "storageKey");
                var deactivateArg = args.FirstOrDefault(a => a.Key == "deactivate");
                val = await RemoveAccount(itemArg.Value, bool.Parse(deactivateArg.Value));
            }
            else if (arg.CommandType == ManagementHubCommands.GetStoredCredentials)
            {
                val = await _credentialsManager.GetCredentials();
            }
            else if (arg.CommandType == ManagementHubCommands.UpdateStoredCredential)
            {
                var args = JsonSerializer.Deserialize<KeyValuePair<string, string>[]>(arg.Value, JsonOptions.DefaultJsonSerializerOptions);
                var itemArg = args.FirstOrDefault(a => a.Key == "item");
                var storedCredential = JsonSerializer.Deserialize<StoredCredential>(itemArg.Value, JsonOptions.DefaultJsonSerializerOptions);

                var updated = await _credentialsManager.Update(storedCredential);
                if (updated != null)
                {
                    val = new ActionResult { IsSuccess = true, Message = "Updated", Result = updated };
                }
                else
                {
                    val = new ActionResult("Update failed", false);
                }
            }
            else if (arg.CommandType == ManagementHubCommands.RemoveStoredCredential)
            {
                var args = JsonSerializer.Deserialize<KeyValuePair<string, string>[]>(arg.Value, JsonOptions.DefaultJsonSerializerOptions);
                var itemArg = args.FirstOrDefault(a => a.Key == "storageKey");
                val = await _credentialsManager.Delete(_itemManager, itemArg.Value);
            }
            else if (arg.CommandType == ManagementHubCommands.UnlockStoredCredential)
            {
                var args = JsonSerializer.Deserialize<KeyValuePair<string, string>[]>(arg.Value, JsonOptions.DefaultJsonSerializerOptions);
                var itemArg = args.FirstOrDefault(a => a.Key == "storageKey");
                var key = itemArg.Value;
                var cred = await _credentialsManager.GetCredential(key);
                if (cred.AllowUnlock)
                {
                    var unlockedCredValue = await _credentialsManager.GetUnlockedCredential(key);
                    if (unlockedCredValue != null)
                    {

                        cred.Secret = unlockedCredValue;
                        val = new StoredCredentialUnlockResult { IsSuccess = true, Result = cred };
                    }
                    else
                    {
                        val = null;
                    }
                }
                else
                {
                    val = new StoredCredentialUnlockResult { IsSuccess = false, Message = "This credential does not allow unlocking" };
                }
            }
            else if (arg.CommandType == ManagementHubCommands.GetChallengeProviders)
            {
                var providers = await Core.Management.Challenges.ChallengeProviders.GetChallengeAPIProviders();
                val = providers;
            }

            else if (arg.CommandType == ManagementHubCommands.GetDnsZones)
            {
                var args = JsonSerializer.Deserialize<KeyValuePair<string, string>[]>(arg.Value, JsonOptions.DefaultJsonSerializerOptions);
                var providerTypeArg = args.FirstOrDefault(a => a.Key == "providerTypeId");
                var credentialIdArg = args.FirstOrDefault(a => a.Key == "credentialId");

                val = await GetDnsProviderZones(providerTypeArg.Value, credentialIdArg.Value);
            }
            else if (arg.CommandType == ManagementHubCommands.GetDeploymentProviders)
            {
                val = await GetDeploymentProviders();
            }
            else if (arg.CommandType == ManagementHubCommands.ExecuteDeploymentTask)
            {
                var args = JsonSerializer.Deserialize<KeyValuePair<string, string>[]>(arg.Value, JsonOptions.DefaultJsonSerializerOptions);

                var managedCertificateIdArg = args.FirstOrDefault(a => a.Key == "managedCertificateId");
                var taskIdArg = args.FirstOrDefault(a => a.Key == "taskId");

                val = await PerformDeploymentTask(null, managedCertificateIdArg.Value, taskIdArg.Value, isPreviewOnly: false, skipDeferredTasks: false, forceTaskExecution: false);
            }
            else if (arg.CommandType == ManagementHubCommands.GetTargetIPAddresses)
            {
                val = await GetTargetIPAddresses();
            }
            else if (arg.CommandType == ManagementHubCommands.GetTargetServiceTypes)
            {
                val = await GetTargetServiceTypes();
            }
            else if (arg.CommandType == ManagementHubCommands.GetTargetServiceItems)
            {
                var args = JsonSerializer.Deserialize<KeyValuePair<string, string>[]>(arg.Value, JsonOptions.DefaultJsonSerializerOptions);
                var serviceTypeArg = args.FirstOrDefault(a => a.Key == "serviceType");

                var serverType = MapStandardServerType(serviceTypeArg.Value);

                val = await GetPrimaryWebSites(serverType, ignoreStoppedSites: true);
            }
            else if (arg.CommandType == ManagementHubCommands.GetTargetServiceItemIdentifiers)
            {
                var args = JsonSerializer.Deserialize<KeyValuePair<string, string>[]>(arg.Value, JsonOptions.DefaultJsonSerializerOptions);
                var serviceTypeArg = args.FirstOrDefault(a => a.Key == "serviceType");
                var itemArg = args.FirstOrDefault(a => a.Key == "itemId");

                var serverType = MapStandardServerType(serviceTypeArg.Value);

                val = await GetDomainOptionsFromSite(serverType, itemArg.Value);
            }
            else if (arg.CommandType == ManagementHubCommands.PerformImport)
            {
                var args = JsonSerializer.Deserialize<KeyValuePair<string, string>[]>(arg.Value, JsonOptions.DefaultJsonSerializerOptions);
                var requestArg = args.FirstOrDefault(a => a.Key == "importRequest");
                var importRequest = JsonSerializer.Deserialize<ImportRequest>(requestArg.Value, JsonOptions.DefaultJsonSerializerOptions);

                val = await PerformImport(importRequest);
            }
            else if (arg.CommandType == ManagementHubCommands.PerformExport)
            {
                var args = JsonSerializer.Deserialize<KeyValuePair<string, string>[]>(arg.Value, JsonOptions.DefaultJsonSerializerOptions);
                var requestArg = args.FirstOrDefault(a => a.Key == "exportRequest");
                var exportRequest = JsonSerializer.Deserialize<ExportRequest>(requestArg.Value, JsonOptions.DefaultJsonSerializerOptions);

                val = await PerformExport(exportRequest);
            }
            else if (arg.CommandType == ManagementHubCommands.GetSystemStatusItems)
            {
                val = _systemStatusItems;
            }
            else if (arg.CommandType == ManagementHubCommands.GetServiceConfig)
            {
                val = _serverConfig;
            }
            else if (arg.CommandType == ManagementHubCommands.GetServiceCoreSettings)
            {
                val = SettingsManager.ToPreferences();
            }
            else if (arg.CommandType == ManagementHubCommands.UpdateServiceCoreSettings)
            {
                var args = JsonSerializer.Deserialize<KeyValuePair<string, string>[]>(arg.Value, JsonOptions.DefaultJsonSerializerOptions);
                var prefUpdate = args.FirstOrDefault(a => a.Key == "prefs");
                var update = JsonSerializer.Deserialize<Preferences>(prefUpdate.Value, JsonOptions.DefaultJsonSerializerOptions);

                var prefs = SettingsManager.ToPreferences();

                if (update != null)
                {
                    // update supported settings
                    prefs.CertificateCleanupMode = update.CertificateCleanupMode;
                    prefs.DefaultACMERetryInterval = update.DefaultACMERetryInterval;
                    prefs.DefaultCertificateAuthority = update.DefaultCertificateAuthority;
                    prefs.DefaultCertificateStore = update.DefaultCertificateStore;
                    prefs.DefaultKeyType = update.DefaultKeyType;
                    prefs.DisableARIChecks = update.DisableARIChecks;

                    prefs.EnableAppTelematics = update.EnableAppTelematics;
                    prefs.EnableAutomaticCAFailover = update.EnableAutomaticCAFailover;
                    prefs.EnableExternalCertManagers = update.EnableExternalCertManagers;
                    prefs.EnableStatusReporting = update.EnableStatusReporting;
                    prefs.EnableValidationProxyAPI = update.EnableValidationProxyAPI;
                    prefs.EnableHttpChallengeServer = update.EnableHttpChallengeServer;

                    prefs.NtpServer = update.NtpServer;
                    prefs.RenewalIntervalDays = update.RenewalIntervalDays;
                    prefs.RenewalIntervalMode = update.RenewalIntervalMode;
                    prefs.UseModernPFXAlgs = update.UseModernPFXAlgs;

                    prefs.CertificateManagers = update.CertificateManagers;

                    SettingsManager.FromPreferences(prefs);

                    try
                    {
                        SettingsManager.SaveAppSettings();
                        val = new ActionResult("Service core settings updated", true);
                    }
                    catch (Exception ex)
                    {
                        _serviceLog.Error(ex, "Error saving preferences");
                        val = new ActionResult("Service core settings could not be updated.", false);
                    }

                    // cert manager config may have changed, refresh required
                    _externallyManagedCacheUpdated = DateTimeOffset.MinValue;
                }
                else
                {
                    val = new ActionResult("Service core settings could not be updated. Invalid data.", false);
                }
            }
            else if (arg.CommandType == ManagementHubCommands.UpdateServiceConfig)
            {
                var args = JsonSerializer.Deserialize<KeyValuePair<string, string>[]>(arg.Value, JsonOptions.DefaultJsonSerializerOptions);
                var configArg = args.FirstOrDefault(a => a.Key == "config");
                var configVal = JsonSerializer.Deserialize<ServiceConfig>(configArg.Value, JsonOptions.DefaultJsonSerializerOptions);
                if (configVal != null)
                {
                    _serverConfig.LogLevel = configVal.LogLevel;
                    _serverConfig.ManagementServerHubAPI = configVal.ManagementServerHubAPI;
                    _serverConfig.ManagementServerHubEndpoint = configVal.ManagementServerHubEndpoint;
                    _serverConfig.UseHTTPS = configVal.UseHTTPS;
                    _serverConfig.Host = configVal.Host;
                    _serverConfig.Port = configVal.Port;
                    _serverConfig.HttpChallengeServerPort = configVal.HttpChallengeServerPort;

                    try
                    {
                        SharedUtils.ServiceConfigManager.StoreUpdatedAppServiceConfig(_serverConfig, throwOnError: true);
                        val = new ActionResult("Service config updated", true);
                    }
                    catch (Exception ex)
                    {
                        _serviceLog.Error(ex, "Error updating service config");
                        val = new ActionResult("Service config could not be updated.", false);
                    }
                }
                else
                {
                    val = new ActionResult("Service config could not be updated. Invalid data.", false);
                }
            }
            else if (arg.CommandType == ManagementHubCommands.ApplyLicense)
            {
                var args = JsonSerializer.Deserialize<KeyValuePair<string, string>[]>(arg.Value, JsonOptions.DefaultJsonSerializerOptions);
                var activationArg = args.FirstOrDefault(a => a.Key == "activation");
                var activation = JsonSerializer.Deserialize<LicenseKeyInstallResult>(activationArg.Value, JsonOptions.DefaultJsonSerializerOptions);
                if (activation != null)
                {
                    var settingsPath = EnvironmentUtil.EnsuredAppDataPath();
                    var productType = _isMgtmHubBackend ? 2 : 1; // 1 = ccm or agent, 2 = hub
                    _licensingManager.FinaliseInstall(productType, activation, settingsPath);

                    await RefreshCachedLicenseCheck();

                    // send updated instance info to hub
                    SendHeartbeatToManagementHub();

                    val = new ActionResult("Activated.", true);
                }
                else
                {
                    val = new ActionResult("Activation failed.", false);
                }
            }
            else if (arg.CommandType == ManagementHubCommands.DeactivateLicense)
            {
                var settingsPath = EnvironmentUtil.EnsuredAppDataPath();
                var productType = _isMgtmHubBackend ? 2 : 1; // 1 = ccm or agent, 2 = hub

                var i = new Models.Shared.RegisteredInstance
                {
                    InstanceId = CoreAppSettings.Current.InstanceId,
                    AppVersion = Management.Util.GetAppVersion().ToString()
                };

                var deactivated = await _licensingManager.DeactivateInstall(productType, settingsPath, null, i);

                if (deactivated)
                {
                    await RefreshCachedLicenseCheck();
                }

                // send updated instance info to hub
                SendHeartbeatToManagementHub();

                val = new ActionResult { IsSuccess = deactivated, Message = deactivated ? "Deactivated." : "Deactivation failed." };

            }
            else if (arg.CommandType == ManagementHubCommands.NotificationAuthenticationRequired)
            {
                _serviceLog.Information("Hub has requested that this instance re-authenticate");
                await _managementServerClient.Disconnect();
            }
            else if (arg.CommandType == ManagementHubCommands.Reconnect)
            {
                _serviceLog.Information("Hub has requested that this instance re-connect");
                await _managementServerClient.Disconnect();
            }

            return new InstanceCommandResult { CommandId = arg.CommandId, Value = JsonSerializer.Serialize(val), ObjectValue = val };
        }

        private StandardServerTypes MapStandardServerType(string type)
        {
            if (StandardServerTypes.TryParse(type, out StandardServerTypes standardServerType))
            {
                return standardServerType;
            }
            else
            {
                return StandardServerTypes.Other;
            }
        }

        private void ReportManagedItemUpdateToMgmtHub(ManagedCertificate item)
        {
            if (item == null || _managementServerClient == null)
            {
                return;
            }

            try
            {
                if (_managementServerClient?.IsConnected() == true)
                {
                    _managementServerClient.SendNotificationToManagementHub(
                        ManagementHubCommands.NotificationUpdatedManagedItem, item);

                    _serviceLog.Debug("Reported managed item update to hub for {itemId}", item.Id);
                }
                else
                {
                    _serviceLog.Warning("Cannot report managed item update - not connected to hub. Update for {itemId} not sent.", item.Id);
                }
            }
            catch (Exception ex)
            {
                _serviceLog.Error(ex, "Failed to report managed item update to hub for {itemId}", item.Id);
            }
        }

        private void ReportManagedItemDeleteToMgmtHub(string id)
        {
            if (_managementServerClient == null)
            {
                return;
            }

            try
            {
                if (_managementServerClient?.IsConnected() == true)
                {
                    _managementServerClient.SendNotificationToManagementHub(
                        ManagementHubCommands.NotificationRemovedManagedItem, id);

                    _serviceLog.Debug("Reported managed item deletion to hub for {itemId}", id);
                }
                else
                {
                    _serviceLog.Warning("Cannot report managed item deletion - not connected to hub. Deletion for {itemId} not sent.", id);
                }
            }
            catch (Exception ex)
            {
                _serviceLog.Error(ex, "Failed to report managed item deletion to hub for {itemId}", id);
            }
        }

        private void ReportRequestProgressToMgmtHub(RequestProgressState progress)
        {
            if (_managementServerClient == null)
            {
                return;
            }

            try
            {
                if (_managementServerClient?.IsConnected() == true)
                {
                    _managementServerClient.SendNotificationToManagementHub(
                        ManagementHubCommands.NotificationManagedItemRequestProgress, progress);

                    _serviceLog.Debug("Reported request progress to hub for {itemId}", progress.ManagedCertificate?.Id);
                }
                else
                {
                    _serviceLog.Debug("Cannot report request progress - not connected to hub. Progress for {itemId} not sent.", progress.ManagedCertificate?.Id);
                }
            }
            catch (Exception ex)
            {
                _serviceLog.Error(ex, "Failed to report request progress to hub for {itemId}", progress.ManagedCertificate?.Id);
            }
        }

        private void _managementServerClient_OnConnectionReconnecting()
        {
            _serviceLog.Warning("Reconnecting to Management Hub...");

            AddSystemStatusItem(
                SystemStatusCategories.SERVICE_CORE,
                SystemStatusKeys.SERVICE_CORE_HUB_CONNECTION,
                "Management Hub Connection",
                "Attempting to reconnect to Management Hub",
                hasWarning: true
            );
        }

        private void _managementServerClient_OnConnectionReconnected()
        {
            _serviceLog.Information("Successfully reconnected to Management Hub");

            AddSystemStatusItem(
                SystemStatusCategories.SERVICE_CORE,
                SystemStatusKeys.SERVICE_CORE_HUB_CONNECTION,
                "Management Hub Connection",
                "Successfully reconnected to Management Hub"
            );

            // Re-register instance with updated information after reconnection
            try
            {
                SendHeartbeatToManagementHub();
                _serviceLog.Debug("Sent heartbeat after reconnection to re-register instance");
            }
            catch (Exception ex)
            {
                _serviceLog.Error(ex, "Failed to send heartbeat after reconnection");
            }
        }

        private void _managementServerClient_OnConnectionClosed()
        {
            _serviceLog.Warning("Management Hub connection closed");

            AddSystemStatusItem(
                SystemStatusCategories.SERVICE_CORE,
                SystemStatusKeys.SERVICE_CORE_HUB_CONNECTION,
                "Management Hub Connection",
                "Connection to Management Hub lost. Will attempt to reconnect.",
                hasError: true
            );
        }

        private async Task GenerateDemoItems(int? numItems)
        {
            var currentItems = await GetManagedCertificateSummary(new ManagedCertificateFilter { Keyword = "DemoData" });
            if (currentItems.Total == 0)
            {
                var items = DemoDataGenerator.GenerateDemoItems(numItems ?? 100, numItems ?? 500);
                foreach (var item in items)
                {
                    _ = UpdateManagedCertificate(item);
                }
            }
        }

        private async Task RandomlyUpdateDemoItems()
        {
            // randomly update status of demo items
            var items = await GetManagedCertificates(new ManagedCertificateFilter { IncludeExternal = true, Keyword = "DemoData" });
            var rand = new Random();

            // randomly update status for a few demo items
            foreach (var item in items)
            {
                if (rand.NextDouble() < 0.02) // 2% chance to update
                {
                    item.LastRenewalStatus = rand.NextDouble() < 0.8 ? RequestState.Success : RequestState.Error;

                    if (item.LastRenewalStatus == RequestState.Error)
                    {
                        item.RenewalFailureCount++;
                        item.RenewalFailureMessage = "Simulated renewal failure for demo purposes.";
                    }
                    else
                    {
                        item.RenewalFailureCount = 0;
                        item.RenewalFailureMessage = null;
                    }

                    item.DateLastRenewalAttempt = DateTimeOffset.UtcNow;

                    _ = UpdateManagedCertificate(item);
                }
            }

            // randomly remove a few demo items

            foreach (var item in items)
            {
                if (rand.NextDouble() < 0.01) // 10% chance to remove
                {
                    _ = DeleteManagedCertificate(item.Id);
                }
            }

            // randomly add a few demo items

            for (var i = 0; i < 5; i++)
            {
                if (rand.NextDouble() < 0.03) // 3% chance to add
                {
                    var newItems = DemoDataGenerator.GenerateDemoItems(1, 1);
                    foreach (var newItem in newItems)
                    {
                        _ = UpdateManagedCertificate(newItem);
                    }
                }
            }
        }
    }
}
